package org.zxt.web.context;

import lombok.extern.slf4j.Slf4j;
import org.zxt.web.context.annotation.RequestUri;
import org.zxt.web.context.handler.ArgumentParser;
import org.zxt.web.context.handler.RequestMapperHandler;
import org.zxt.web.context.handler.parser.RequestUriParser;
import org.zxt.web.context.handler.parser.WebContextParser;
import org.zxt.web.nio.*;

import java.io.*;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static org.zxt.web.nio.NioHTTPD.*;

@Slf4j
public class StaticRequestMapperHandler implements RequestMapperHandler {
    public static final String ROUTER = "/";
    private String resourcePath;
    public StaticRequestMapperHandler(String path){
        this.resourcePath = path;
    }
    @Override
    public Object[] parseArguments(WebContext webContext, IHTTPSession session) {
        return new Object[]{
                session,
                session.getUri()
        };
    }

    public Response handler(HTTPSession session, @RequestUri String url) {
        if(url.equals("/") || url.equals("") || !url.contains(".")){
            url = "/index.html";
        }
        if(!url.startsWith("/")){
            url = "/"+url;
        }
        try {
            Path filePath = Paths.get(resourcePath+url);
            File file = new File(resourcePath+url);
            if(filePath == null){
                throw new FileNotFoundException(url);
            } else {
                boolean isGzip = session.getHeaders().get("accept-encoding").contains("gzip") && file.length() > 1024;
                String finalUrl = url;
                if(!file.exists()){
                    return new Response(session, Status.NOT_FOUND, MIME_HTML, "404");
                }
                return new Response(session, Status.OK, getMimeTypeForFile(finalUrl), null, file.length()){
                    @Override
                    public void sendBody(OutputStream outputStream, long pending, boolean encodeAsGzip) {
                        int len = 0;
                        try (FileChannel channel = FileChannel.open(filePath)) {
                            ByteBuffer bu = ByteBuffer.allocate(4096);
                            while ((len = channel.read(bu)) > 0) {
                                bu.flip();
                                if(!encodeAsGzip) {
                                    byte[] bs = bu.array();
                                    int sy = len;
                                    int sendLen = this.getSocketChannel().write(ByteBuffer.wrap(bs, 0, len));
                                    sy -= sendLen;
                                    while (sy != 0) {
                                        sendLen = this.getSocketChannel().write(ByteBuffer.wrap(bs, sendLen, len));
                                        sy -= sendLen;
                                    }
                                    pending -= len;
                                    if (pending == 0) {
                                        log.debug(this + "write end:" + finalUrl);
                                        return;
                                    }
                                } else {
                                    outputStream.write(bu.array(),0, len);
                                }
                                bu.clear();
                            }
                        } catch (Exception ex){
                            ex.printStackTrace();
                        }
                    }
                }.setGzipEncoding(isGzip);
            }
        } catch (IOException ex){
            return new Response(session, Status.NOT_FOUND, MIME_HTML, "<html><body>404</body></html>");
        }
    }
    public static final Method HANDLER;

    static {
        try {
            HANDLER = StaticRequestMapperHandler.class.getDeclaredMethod("handler", HTTPSession.class, String.class);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Method getInvoker() {
        return HANDLER;
    }

    @Override
    public Object getTarget() {
        return this;
    }
}
